import { defineStore } from "pinia"
import { reactive, watch } from "vue"
import {
    Entity,
    LearningProgress,
    Meaning,
    MeaningProgress,
    ProgressType,
    WordProgresses,
} from "../model"
import { useConfigStore } from "./config"
import { addProgress as add, getData, updateProgress } from "./db"

export const useProgressStore = defineStore("progress", () => {
    const progresses: Entity<LearningProgress>[] = reactive([])
    const skilledProgresses: Entity<LearningProgress>[] = reactive([])
    const unskilledProgresses: Entity<LearningProgress>[] = reactive([])
    const wordProgressesMap: Map<string, WordProgresses> = new Map()
    const unskilledProgressMap: Map<string, Entity<LearningProgress>[]> =
        new Map()

    function _addProgress(p: Entity<LearningProgress>) {
        p = reactive(p)
        progresses.push(p)
        const { config } = useConfigStore()
        if (p.exp >= config.skilledRequiredExp) {
            skilledProgresses.push(p)
        } else {
            unskilledProgresses.push(p)
            let ps = unskilledProgressMap.get(p.word)
            if (ps == undefined) {
                unskilledProgressMap.set(p.word, [p])
            } else {
                ps.push(p)
            }
        }
        let wp = wordProgressesMap.get(p.word) as WordProgresses
        if (wp == undefined) {
            wp = { meaning: [] } as any
            wordProgressesMap.set(p.word, wp as any)
        }
        switch (p.type) {
            case ProgressType.LISTEN:
                wp.listen = p
                break
            case ProgressType.SPEAK:
                wp.speak = p
                break
            case ProgressType.WORD_MEANING:
                wp.meaning.push(p)
                break
            case ProgressType.SPELL:
                wp.spell = p
                break
            default:
                ;((x: never) => x)(p)
        }
        watchExpChange(p)
        watch(p, updateProgress)
    }

    const data = getData()
    data.progresses.forEach(_addProgress)

    async function addProgress(progress: LearningProgress) {
        let p = await add(reactive(progress))
        _addProgress(p)
    }

    function watchExpChange(progress: Entity<LearningProgress>) {
        const { config } = useConfigStore()
        const skilledExp = config.skilledRequiredExp
        watch(
            () => progress.exp,
            (exp, oldExp) => {
                if (exp >= skilledExp && oldExp < skilledExp) {
                    const unskilledProgressesIndex =
                        unskilledProgresses.indexOf(progress)
                    if (unskilledProgressesIndex >= 0) {
                        unskilledProgresses.splice(unskilledProgressesIndex, 1)
                    }
                    const lps = unskilledProgressMap.get(progress.word)
                    if (lps) {
                        const lpsIndex = lps.indexOf(progress)
                        if (lpsIndex >= 0) {
                            lps.splice(lpsIndex, 1)
                        }
                        if (lps.length < 1)
                            unskilledProgressMap.delete(progress.word)
                    }
                    skilledProgresses.push(progress)
                } else if (exp < skilledExp && oldExp >= skilledExp) {
                    const skilledProgressesIndex =
                        skilledProgresses.indexOf(progress)
                    if (skilledProgressesIndex >= 0) {
                        skilledProgresses.splice(skilledProgressesIndex, 1)
                    }
                    unskilledProgresses.push(progress)
                    let lps = unskilledProgressMap.get(progress.word)
                    if (lps == undefined) {
                        lps = []
                        unskilledProgressMap.set(progress.word, lps)
                    }
                    lps.push(progress)
                }
            }
        )
    }

    function getWordProgresses(word: string): WordProgresses {
        const wordProgresses = wordProgressesMap.get(word)
        if (!wordProgresses) throw `WordProgresses not find of: ${word}`
        return wordProgresses
    }

    function getWordMeaningProgress(
        word: string,
        meaning: Meaning
    ): MeaningProgress {
        const r = getWordProgresses(word).meaning.find((progress) => {
            if (progress.meaning == meaning) return progress
        })
        if (r) return r
        throw `MeaningProgress not find: word:${word}, meaning: ${meaning.meaning}`
    }

    function getColor(exp: number) {
        const { config } = useConfigStore()
        const colorLen = Object.keys(config.progressColors).length
        const step = config.skilledRequiredExp / colorLen
        if (exp > config.skilledRequiredExp) {
            exp = config.skilledRequiredExp
        }
        const a = exp / step
        let colorIndex: number
        let opacity: number
        if (a > 0 && a == Math.trunc(a)) {
            colorIndex = a - 1
            opacity = 1
        } else {
            colorIndex = Math.trunc(a)
            opacity = 0.5 + (a - colorIndex) / 2
        }
        return {
            color: config.progressColors[colorIndex],
            opacity,
        }
    }

    function getProgressColor(p: Entity<LearningProgress>) {
        return getColor(p.exp)
    }

    function getWordColor(w: string) {
        const { config } = useConfigStore()
        try {
            const progress = getWordProgresses(w)
            function getExp(exp: number) {
                return exp > config.skilledRequiredExp
                    ? config.skilledRequiredExp
                    : exp
            }

            let exp = 0
            exp += getExp(progress.listen.exp)
            exp += getExp(progress.speak.exp)
            exp += getExp(progress.spell.exp)
            exp += progress.meaning
                .map((p) => getExp(p.exp))
                .reduce((p, c) => p + c)
            exp /= 3 + progress.meaning.length
            return getColor(exp)
        } catch (e) {
            console.error(e)
            return {}
        }
    }

    return {
        progresses,
        skilledProgresses,
        unskilledProgresses,
        unskilledProgressMap,
        wordProgressesMap,
        addProgress,
        getWordProgresses,
        getWordMeaningProgress,
        getProgressColor,
        getWordColor,
    }
})
